package com.mc.lp.database.service.handler.user;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.annotation.Resource;

import org.springframework.util.CollectionUtils;

import com.mc.lp.database.cache.CacheServiceType;
import com.mc.lp.database.cache.CertificationCache;
import com.mc.lp.database.entity.type.SchoolGrade;
import com.mc.lp.database.exception.DataNotFoundException;
import com.mc.lp.database.service.IAttachmentService;
import com.mc.lp.database.service.handler.AbstractFormHandler;
import com.mc.lp.database.service.handler.constant.FormRequestType;
import com.mc.lp.database.service.handler.util.Group;
import com.mc.lp.database.service.handler.util.Search;
import com.mc.lp.database.service.handler.util.SearchBy;
import com.mc.lp.database.service.handler.util.group.GroupByExam;
import com.mc.lp.database.service.handler.util.group.GroupByGradeId;
import com.mc.lp.database.service.handler.util.group.SearchByExamId;
import com.mc.lp.service.ExtraAcademicService;
import com.mc.lp.service.MicroCampusEntityService;
import com.mc.lp.service.RecommendationService;
import com.mc.lp.shared.util.constants.ResponseStatus;
import com.mc.lp.shared.vo.ExtraAcademicRecordVO;
import com.mc.lp.shared.vo.IValueObject;
import com.mc.lp.shared.vo.KeyValue;
import com.mc.lp.shared.vo.RecommendationVO;
import com.mc.lp.shared.vo.SearchCriteriaVO;
import com.mc.lp.shared.vo.ServiceResponse;
import com.mc.lp.shared.vo.user.CommonGridVo;
import com.mc.lp.shared.vo.user.bubble.BubbleRow;
import com.mc.lp.shared.vo.user.bubble.BubbleTable;
import com.mc.lp.shared.vo.user.bubble.BubbleValue;

/*
 * @author aphanda
 * @date Mar 10, 2015
 * @time 9:57:18 PM
 */

public class UserExtraAcademicDashboardHandler extends AbstractFormHandler<IValueObject, IValueObject> {

	@Resource
	private ExtraAcademicService extraAcademicRecordService;

	@Resource
	private MicroCampusEntityService microCampusEntityService;

	@Resource
	private RecommendationService recommendationService;

	@Resource
	private IAttachmentService attachmentService;

	@Override
	public HashSet<FormRequestType> getRequestHandlingType() {
		HashSet<FormRequestType> handledRequest = new HashSet<FormRequestType>();
		handledRequest.add(FormRequestType.USER_EXTRA_ACADEMICS_DASHBOARD_OVERALL_GRID);
		handledRequest.add(FormRequestType.USER_EXTRA_ACADEMICS_DASHBOARD_OVERALL_RECOMMENDATION);
		handledRequest.add(FormRequestType.USER_EXTRA_ACADEMICS_DASHBOARD_OVERALL_STRENGTH);
		return handledRequest;
	}
	
	

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.mc.lp.database.service.handler.AbstractFormHandler#serveRequest(com
	 * .mc.lp.database.service.handler.constant.FormRequestType,
	 * com.mc.lp.shared.vo.IValueObject)
	 */
	@Override
	public IValueObject serveRequest(FormRequestType requestType, IValueObject object) throws Exception {
		switch (requestType) {
		case USER_EXTRA_ACADEMICS_DASHBOARD_OVERALL_GRID:
			return getOverallGrid(requestType, (SearchCriteriaVO) object);
		case USER_EXTRA_ACADEMICS_DASHBOARD_OVERALL_RECOMMENDATION:
			return getRecommendationsForStudent(requestType, (SearchCriteriaVO) object);
		case USER_EXTRA_ACADEMICS_DASHBOARD_OVERALL_STRENGTH:
			return getStrengthAndWeaknessData(requestType, (SearchCriteriaVO) object);
		default:
			return null;
		}
	}

	private IValueObject getStrengthAndWeaknessData(FormRequestType requestType, SearchCriteriaVO object) {
		CertificationCache certificatesCache = (CertificationCache) getCacheService().getService(CacheServiceType.CERTIFICATION);
		ServiceResponse<BubbleTable> serviceResponse = new ServiceResponse<BubbleTable>();
		List<Integer> gradeValueList = getGradesByPageNumAndLimit(object,true);
		BubbleTable table = new BubbleTable();
		

		if (gradeValueList == null) {
			serviceResponse.setStatus(ResponseStatus.NOT_FOUND);
			return serviceResponse;
		}

		List<ExtraAcademicRecordVO> extraAcadList = extraAcademicRecordService.getExtraAcademicsByGrades(object.getStudentEntityId(), gradeValueList);
		if (extraAcadList == null || extraAcadList.isEmpty()) {
			serviceResponse.setStatus(ResponseStatus.NOT_FOUND);
			return serviceResponse;
		}
		
		//get all grades here
		HashMap<Integer, List<ExtraAcademicRecordVO>> groupByGrade = Group.unique(extraAcadList, new GroupByGradeId());
		
		//get all exams here
		HashMap<Long, List<ExtraAcademicRecordVO>> groupByExam = Group.unique(extraAcadList, new GroupByExam());
		
		
		//get all grade id in sorted order
		List<Integer> gradeIdList= new ArrayList<Integer>();
		List<String> gradeNameList= new ArrayList<String>();
		for(Integer grade:groupByGrade.keySet()){
			gradeIdList.add(grade);
		}
		
		Collections.sort(gradeIdList);
		//get grade name and send to UI
		for(Integer grade:gradeIdList){
			gradeNameList.add(SchoolGrade.getSchoolGrade(grade).getValue());
		}
		table.setxAxis(gradeNameList);
		
		//get all exams
		ArrayList<Long> examIds= new ArrayList<Long>();
		for(Long examId:groupByExam.keySet()){
			examIds.add(examId);
		}
		Collections.sort(examIds);
		
		//now loop each exam and check if exists for all grades or not and prepare row.
		ArrayList<BubbleRow> list = new ArrayList<BubbleRow>();
		for(Long examiniationId:examIds){
			BubbleRow row = new BubbleRow();
			row.setName(certificatesCache.getEntityById(examiniationId).getCertificationName());
			ArrayList<BubbleValue> bubbleValueList = new ArrayList<BubbleValue>();	
			//search in each grade
			for(Integer gradeId: gradeIdList){
				BubbleValue value = new BubbleValue();
				List<ExtraAcademicRecordVO> examRecord= groupByGrade.get(gradeId);
				ExtraAcademicRecordVO record= Search.search(examRecord, new SearchByExamId(), examiniationId);
				value.setFlag((record==null)?false:true);
				
				bubbleValueList.add(value);
			}
			row.setValues(bubbleValueList); //add all values
			list.add(row); //add row 
		}
		table.setRows(list);
		serviceResponse.setStatus(ResponseStatus.SUCCESS);
		serviceResponse.setResult(Arrays.asList(table));
		return serviceResponse;
	}

	private IValueObject getRecommendationsForStudent(FormRequestType requestType, SearchCriteriaVO object) {
		ServiceResponse<RecommendationVO> serviceResponse = new ServiceResponse<RecommendationVO>();
		List<RecommendationVO> recommendationVOS;
		try {
			recommendationVOS = recommendationService.getRecommendationBySearchCriteria(object);
			if (recommendationVOS == null || recommendationVOS.isEmpty()) {
				serviceResponse.setStatus(ResponseStatus.NOT_FOUND);
				return serviceResponse;
			}

			serviceResponse.setResult(recommendationVOS);
			serviceResponse.setExtraInfo(new KeyValue("class", SchoolGrade.getSchoolGradeName(object.getGradeId())));
			serviceResponse.setStatus(ResponseStatus.SUCCESS);
		} catch (DataNotFoundException e) {
			serviceResponse.setStatus(ResponseStatus.NOT_FOUND);
		} catch (Exception e) {
			serviceResponse.setStatus(ResponseStatus.FAILED);
		}
		return serviceResponse;
	}

	@SuppressWarnings("unchecked")
	private IValueObject getOverallGrid(FormRequestType requestType, SearchCriteriaVO object) {

		ServiceResponse<CommonGridVo<ExtraAcademicRecordVO>> serviceResponse = new ServiceResponse<CommonGridVo<ExtraAcademicRecordVO>>();
		List<Integer> gradeValueList = getGradesByPageNumAndLimit(object);

		if (gradeValueList == null) {
			serviceResponse.setStatus(ResponseStatus.NOT_FOUND);
			return serviceResponse;
		}

		List<ExtraAcademicRecordVO> extraAcadList = extraAcademicRecordService.getExtraAcademicsByGrades(object.getStudentEntityId(), gradeValueList);
		if (extraAcadList == null || extraAcadList.isEmpty()) {
			serviceResponse.setStatus(ResponseStatus.NOT_FOUND);
			return serviceResponse;
		}

		TreeMap<Integer, List<ExtraAcademicRecordVO>> extraAcademicViewMap = new TreeMap<Integer, List<ExtraAcademicRecordVO>>();
		// group extra academic based on grades.
		if (!CollectionUtils.isEmpty(extraAcadList)) {
			for (ExtraAcademicRecordVO record : extraAcadList) {
				List<ExtraAcademicRecordVO> existingRecord = extraAcademicViewMap.get(record.getGradeId());
				// no record found add
				if (CollectionUtils.isEmpty(existingRecord)) {
					existingRecord = new ArrayList<ExtraAcademicRecordVO>();
					existingRecord.add(record);
					extraAcademicViewMap.put(record.getGradeId(), existingRecord);
				} else {
					existingRecord.add(record);
					extraAcademicViewMap.put(record.getGradeId(), existingRecord);
				}
			}
		}
		// convert to UI Vo
		CommonGridVo<ExtraAcademicRecordVO> commonGridVo = new CommonGridVo<ExtraAcademicRecordVO>();
		List<KeyValue> gradeList = new ArrayList<KeyValue>();

		if (!CollectionUtils.isEmpty(extraAcademicViewMap))
			for (Entry<Integer, List<ExtraAcademicRecordVO>> entry : extraAcademicViewMap.entrySet()) {
				Integer gradeId = entry.getKey();
				if (gradeId != null)
					gradeList.add(new KeyValue(SchoolGrade.getSchoolGradeName(gradeId), entry.getValue()));
			}

		commonGridVo.setGradeList(gradeList);
		commonGridVo.setObjects(extraAcadList);

		serviceResponse.setStatus(ResponseStatus.SUCCESS);
		serviceResponse.setResult(Arrays.asList(commonGridVo));

		return serviceResponse;

	}
} 